package com.empress.uranus.zma.task;

import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.TimeUnit;

import javax.annotation.Resource;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.stereotype.Service;

import com.empress.uranus.zma.biz.machine.HeartbeatServerHandlerBiz;
import com.empress.uranus.zma.biz.machine.MCInvokeInboundHandlerBiz;
import com.empress.uranus.zma.biz.machine.MCInvokeOutboundHandlerBiz;
import com.empress.uranus.zma.biz.machine.MCServerInboundHandlerBiz;
import com.empress.uranus.zma.biz.machine.MCServerOutboundHandlerBiz;
import com.empress.uranus.zma.common.constants.MCConstant;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.timeout.IdleStateHandler;

/**
 * 任务处理类
 */
@Service("napkinServerHandlerTask")
public class NapkinServerHandlerTask implements InitializingBean {
	
	private static final Logger LOGGER = LoggerFactory.getLogger(NapkinServerHandlerTask.class);
	
	@Resource(name="heartbeatServerHandlerBiz")
	private HeartbeatServerHandlerBiz heartbeatServerHandlerBiz;
	
	@Resource(name="mcserverInboundHandlerBiz")
	private MCServerInboundHandlerBiz mcserverInboundHandlerBiz;
	
	@Resource(name="mcinvokeInboundHandlerBiz")
	private MCInvokeInboundHandlerBiz mcinvokeInboundHandlerBiz;
	
	@Resource(name="mcserverOutboundHandlerBiz")
	private MCServerOutboundHandlerBiz mcserverOutboundHandlerBiz;
	
	@Resource(name="mcinvokeOutboundHandlerBiz")
	private MCInvokeOutboundHandlerBiz mcinvokeOutboundHandlerBiz;

	@Override
	public void afterPropertiesSet() throws Exception {
		new Timer().schedule(new TimerTask() {
			@Override
			public void run() {
				LOGGER.info("start netty napkin server handler task");
				EventLoopGroup boss = new NioEventLoopGroup();
				EventLoopGroup worker = new NioEventLoopGroup();
				ServerBootstrap bootstrap=new ServerBootstrap();
				bootstrap.group(boss, worker);
				bootstrap.channel(NioServerSocketChannel.class);
				//BACKLOG用于构造服务端套接字ServerSocket对象，标识当服务器请求处理线程全满时，用于临时存放已完成三次握手的请求的队列的最大长度
				bootstrap.option(ChannelOption.SO_BACKLOG, 128);
				//通过NoDelay禁用Nagle,使消息立即发出去，不用等待到一定的数据量才发出去
				bootstrap.option(ChannelOption.TCP_NODELAY, true);
				//是否启用心跳保活机制。在双方TCP套接字建立连接后（即都进入ESTABLISHED状态）并且在两个小时左右上层没有任何数据传输的情况下，这套机制才会被激活
				bootstrap.childOption(ChannelOption.SO_KEEPALIVE, true);
				bootstrap.childHandler(new ChannelInitializer<SocketChannel>() {

		            @Override
		            protected void initChannel(SocketChannel socketChannel) throws Exception {
		            	//注册两个OutboundHandler，两个InboundHandler
		                ChannelPipeline pipeLine = socketChannel.pipeline();
		                //先执行MCInvokeOutboundHandler，再执行MCServerOutboundHandler
		                pipeLine.addLast(mcserverOutboundHandlerBiz);
		                pipeLine.addLast(mcinvokeOutboundHandlerBiz);

		                //服务器心跳机制，当取票机客户端长时间没有与取票机进行交互时，断开取票机的channel，迫使取票机重新建立链接
		                //服务器一直都是保活的
		                pipeLine.addLast(new IdleStateHandler(3, 0, 0, TimeUnit.HOURS));
		                pipeLine.addLast(heartbeatServerHandlerBiz);
		                //先执行MCServerInboundHandler，再执行MCInvokeInboundHandler
		                pipeLine.addLast(mcserverInboundHandlerBiz);
		                pipeLine.addLast(mcinvokeInboundHandlerBiz);
		            }
		        });
				try {
					ChannelFuture channelFuture = bootstrap.bind(MCConstant.NETTY_SERVER_PORT).sync();
					if(channelFuture.isSuccess()){
						LOGGER.info("ticket taked machine server has started successfully");
					}
					channelFuture.channel().closeFuture().sync();
				} catch (InterruptedException ex) {
					
					LOGGER.error("纸巾机服务器绑定客户端时抛出了异常，端口{},", MCConstant.NETTY_SERVER_PORT, ex);
				} finally {
					
					worker.shutdownGracefully();
					boss.shutdownGracefully();
				}
				LOGGER.info("纸巾机服务器关闭，{}秒后尝试重新启动", MCConstant.SCHEDULE_EXECUTE_PERIOD/1000);
			}
		}, MCConstant.SCHEDULE_INIT_DELAY, MCConstant.SCHEDULE_EXECUTE_PERIOD);
	}
}
